﻿using System;
using System.Collections;
using System.Collections.Generic;
using Gp.Scripts.Core.Unit;
using Gp.Scripts.Data;
using QFramework;
using UnityEngine;

namespace Gp.Scripts.Core
{
    /// <summary>
    /// 单位的实体。具有播放动画、计算数值、施加状态、发动能力等接口。
    /// </summary>
    public abstract class BaseUnit : BaseEntity
    {
        public int ID => EntityConfig.GetID();
        public string UnitName => EntityConfig.Name;
        public string Description => EntityConfig.Desc;

        
        /// <summary>
        /// 作用域为单位内部的EventSystem
        /// </summary>
        private TypeEventSystem _innerEventSystem;

        public override string Guid => EntityBehaviour.Guid;


        public bool IsInit { get; private set; } = false;

        #region Property

        public UnitStatCom ComStat { get; private set; }
        public UnitCostCom ComCost { get; private set; }
        public UnitSkillCom ComSkill { get; private set; }
        public UnitBuffCom ComBuff { get; private set; }
        public UnitEquipCom ComEquip { get; private set; }
        public UnitProCom ComPro { get; private set; }
        public UnitModel Model { get; private set; }


        public int PlayerNumber = 0;

        #endregion


        #region Init

        public void StartBattle()
        {
            TriggerEvent<Event_Unit.BattleStart>(false);
        }

        public void EndBattle()
        {
            TriggerEvent<Event_Unit.BattleEnd>(false);
        }

        protected sealed override void OnInitialize()
        {
            if (this.IsInit) return;
            tag          = SceneTagConst.GameEntity;
            PlayerNumber = (int) this.EntityBehaviour.campNumber;

            if (EntityConfig == null)
            {
                throw new Exception("No prefab data.");
            }

            _innerEventSystem = new TypeEventSystem();

            ComStat  = new UnitStatCom(this);
            ComSkill = new UnitSkillCom();
            ComBuff  = new UnitBuffCom();
            ComEquip = new UnitEquipCom();
            ComPro   = new UnitProCom();


            ComSkill.Init(this);
            ComBuff.Init(this);
            ComPro.Init(this);
            ComEquip.Init(this);


            // 行动资源容器
            ComCost = new UnitCostCom(this);

            InitViewComponent();

            // 执行组件全部载入完成的回调（子类更改数据）
            AfterComponentInit();


            ComCost.OnBattleStart();
            ComSkill.AfterInit();

            this.IsInit = true;
        }


        /// <summary>
        /// 在所有组件都完成初始化后调用的回调。
        /// </summary>
        public virtual void AfterComponentInit() { }


        /// <summary>
        ///  初始化视觉层模块
        /// </summary>
        private void InitViewComponent()
        {
            var model = transform.GetComponentInChildren<UnitModel>();
            if (model != null)
            {
                Model = model;
            }
            else
            {
                var prefab    = ResLoader.LoadSync<UnitModel>(ConstBundle.UNIT_MODEL, base.EntityConfig.ModelName);
                var unitModel = prefab.Instantiate();
                unitModel.name = "Unit Model";
                Model          = unitModel;

                unitModel.transform.SetParent(this.transform, true);
            }

            Model.Init(this);
        }


        private static string[] _subObjectsName;
        public List<UnitSubObject> SubObjectsContainer { get; } = new();


        protected override void OnDestroy()
        {
            ComEquip.OnDestroy();
            ComPro.OnDestroy();
            ComBuff.OnDestroy();
            ComSkill.OnDestroy();

            ComSkill          = null;
            ComBuff           = null;
            ComEquip          = null;
            ComPro            = null;
            ComStat           = null;
            ComCost           = null;
            _innerEventSystem = null;
        }

        #endregion


        #region TurnState

        public IEnumerator TriggerTurnStart()
        {
            ComCost.OnTurnStart();
            yield return ComEquip.OnTurnStart();
            yield return ComSkill.OnTurnStart();
            yield return ComBuff.OnTurnStart();
            yield return ComPro.OnTurnStart();
        }

        public IEnumerator TriggerTurnEnd()
        {
            // 更新顺序 ： 资源更新 -> 技能更新 -> Buff更新
            ComCost.OnTurnEnd();
            yield return ComEquip.OnTurnEnd();
            // 因此可以在回合开始时通过buff修改资源回复量
            yield return ComSkill.OnTurnEnd();
            yield return ComBuff.OnTurnEnd();
            yield return ComPro.OnTurnEnd();
        }

        #endregion


        #region Model

        /// <summary>
        /// 将单位标记为某个状态，如：选中、未选中、恐慌
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void MarkAs<T>() where T : UnitModelStateMark, new() => Model.MarkAs<T>();

        public void MarkAs<T>(T stateMark) where T : UnitModelStateMark => Model.MarkAs(stateMark);

        /// <summary>
        /// 播放序列：如攻击动画、死亡动画
        /// </summary>
        public IEnumerator PlaySequence<T>() where T : UnitGestureSequence, new() => Model.PlaySequence<T>();

        public IEnumerator PlaySequence<T>(T g) where T : UnitGestureSequence => Model.PlaySequence(g);

        /// <summary>
        /// 触发动画，特点是可以多次重复调用
        /// </summary>
        public void GestureTrigger<T>() where T : UnitGestureTrigger, new() => Model.GestureTrigger<T>();

        public void GestureTrigger<T>(T g) where T : UnitGestureTrigger => Model.GestureTrigger(g);

        #endregion


        #region Stat

        public override IntValue CurHealth => ComCost.GetResourceWrapper(CostField.Health);
        public override IntValue MaxHealth => ComStat.GetValueWrapper(StatField.MaxHealth);


        public override void ReceiveDamage(int damage)
        {
            this.ComCost.ReduceResource(CostField.Health, damage);
        }

        public override void ReceiveCure(int cure) { }

        public bool AddBuff(BaseBuff buff)
        {
            return buff != null && ComBuff.AddBuff(buff);
        }

        public override bool ContainsTag(int tags)
        {
            return false;
        }

        #endregion


        #region Event

        public sealed override void MarkHighlight()
        {
            this.TriggerEvent<Event_Unit.Highlight>();
        }

        public sealed override void MarkDeHighlight()
        {
            this.TriggerEvent<Event_Unit.DeHighlight>();
        }

        public sealed override void OnEntitySelect()
        {
            this.TriggerEvent<Event_Unit.Select>();
        }


        /// <summary>
        /// 触发某个单位事件。在触发时，会同时向单位内和全局发送。<br/>
        /// <para>
        /// <b>单位内部作用域：</b>面向单位内部机制，如只作用于该单位的buff，不关心该单位外的其他单位。<br/>
        /// <b>全局作用域：</b>面向全局机制和UI显示
        /// </para>
        /// </summary>
        /// <param name="isGlobalEvent">是否为全局事件</param>
        public void TriggerEvent<TEvent>(bool isGlobalEvent = true) where TEvent : Event_Unit, new()
        {
            TriggerEvent(new TEvent(), isGlobalEvent);
        }

        /// <summary>
        /// 触发某个单位事件。在触发时，会同时向单位内和全局发送。<br/>
        /// <para>
        /// <b>单位内部作用域：</b>面向单位内部机制，如只作用于该单位的buff，不关心该单位外的其他单位。<br/>
        /// <b>全局作用域：</b>面向全局机制和UI显示
        /// </para>
        /// </summary>
        /// <param name="e">事件参数</param>
        /// <param name="isGlobalEvent">是否为全局事件</param>
        public void TriggerEvent<TEvent>(TEvent e, bool isGlobalEvent = true) where TEvent : Event_Unit
        {
            e.Signature(this);
            _innerEventSystem.Send(e);
            if (isGlobalEvent) Global.Get<BattleSystem>().SendEvent(e);
        }


        /// <summary>
        /// 以该单位内部为作用域注册一个事件
        /// </summary>
        public IUnRegister RegisterEvent<TEvent>(Action<TEvent> onEvent) where TEvent : Event_Unit
        {
            return _innerEventSystem.Register<TEvent>(onEvent);
        }


        /// <summary>
        /// 以该单位内部为作用域注册一个事件
        /// </summary>
        public void UnRegisterEvent<TEvent>(Action<TEvent> onEvent) where TEvent : Event_Unit
        {
            _innerEventSystem.UnRegister<TEvent>(onEvent);
        }

        #endregion
    }
}